#include "TypeDescription.h"
#include "NamespaceDescription.h"
#include "ConstructorDescription.h"

#include "DecoratedTypeDescription.h"
#include "ParameterDescription.h"

std::shared_ptr<CFunctionDescription> CTypeDescription::FindFunctionByNameAndType(const CString &InFunctionName, SDecoratedTypeDescription InFunctionType)
{
    for (auto& Function : Functions)
    {
        if (Function->GetSearchName() != InFunctionName)
        {
            continue;
        }

        if(!Function->ReturnType.IsSameWith(InFunctionType.BasicType->FunctionPointerReturnType))
        {
            continue;
        }

        if(Function->Parameters.Num()!=InFunctionType.BasicType->FunctionPointerParameters.Num())
        {
            continue;
        }

        for(int i=0;i<Function->Parameters.Num();++i)
        {
            if(!Function->Parameters[i]->Type.IsSameWith(InFunctionType.BasicType->FunctionPointerParameters[i]))
            {
                continue;
            }
        }

        return Function;
    }
    
    if(BaseClass)
    {
        return BaseClass->FindFunctionByNameAndType(InFunctionName,InFunctionType);
    }

    return nullptr;    
}

std::shared_ptr<CTypeDescription> CTypeDescription::FindClassInFile(
    std::shared_ptr<CScriptFileContext> InFile, const CString &InClassName)
{

    //try find in this class
    {
        auto FoundClass= FindClass(InClassName);

        if(FoundClass)
        {
            return FoundClass;
        }
    }

    if(!ParentScope.Type.expired())
    {
        //parent is class
        auto ParentClass= ParentScope.Type.lock();
        return ParentClass->FindClassInFile(InFile,InClassName);
    }           
    else 
    {
        assert(!ParentScope.Namespace.expired());
        auto ParentNamespace = ParentScope.Namespace.lock();
        return ParentNamespace->FindClassInFile(InFile,InClassName);
    }             
}

std::shared_ptr<CNamespaceDescription> CTypeDescription::FindNamespaceInFile(std::shared_ptr<CScriptFileContext> InFile, const CString &InNamespaceName)
{
    if(!ParentScope.Type.expired())
    {
        //parent is class
        auto ParentClass= ParentScope.Type.lock();
        return ParentClass->FindNamespaceInFile(InFile,InNamespaceName);
    }           
    else
    {
        assert(!ParentScope.Namespace.expired());
        auto ParentNamespace = ParentScope.Namespace.lock();
        return ParentNamespace->FindNamespaceInFile(InFile,InNamespaceName);
    }   
}

CString CTypeDescription::GetFullName() const
{

    assert(IsGenerailizedClass()==ParentScopeNameChain.Empty()
        &&"特化的类型不能有父命名范围");

    if(ParentScopeNameChain.Empty())
    {
        return Name;
    }

    CString FullName;
    for(int32_t i=0;i<ParentScopeNameChain.Num();++i)
    {
        FullName+=ParentScopeNameChain[i];
        FullName+=U"::";
    }

    FullName+=Name;

    return FullName;
}

std::shared_ptr<CConstructorDescription> CTypeDescription::GetDelegateConstructorByFunctionPointer()
{
    auto Constructor=Constructors[0];
    assert(Constructor && "Delegate must have a constructor");
    assert(Constructor->Parameters.Num()==2);
    assert(Constructor->Parameters[0]->Type.BasicType->IsClass());
    assert(Constructor->Parameters[1]->Type.BasicType->IsFunctionPointer());
    return Constructor;
}

std::shared_ptr<CFunctionDescription> CTypeDescription::GetDelegateInvokeFunction()
{
    auto Function=Functions[0];
    assert(Function && "Delegate must have a function");
    assert(Function->Parameters.Num()==
        DelegateFunctionPointerType.BasicType->FunctionPointerParameters.Num());
    return Function;
}

std::shared_ptr<CFunctionDescription> CTypeDescription::GetDelegateBindFunctionPointerFunction()
{
    auto Function=Functions[1];
    assert(Function && "Delegate must have a function");
    assert(Function->Parameters.Num()==2);
    assert(Function->Parameters[0]->Type.BasicType->IsClass());
    assert(Function->Parameters[1]->Type.BasicType->IsFunctionPointer());
    return Function;
}

std::shared_ptr<CFunctionDescription> CTypeDescription::GetDelegateUnbindFunctionPointerFunction()
{
    auto Function=Functions[2];
    assert(Function && "Delegate must have a function");
    assert(Function->Parameters.Num()==2);
    assert(Function->Parameters[0]->Type.BasicType->IsClass());
    assert(Function->Parameters[1]->Type.BasicType->IsFunctionPointer());
    return Function;
}

std::shared_ptr<CFunctionDescription> CTypeDescription::GetDelegateAppendFunction()
{
    auto Function=Functions[3];
    assert(Function && "Delegate must have a function");
    assert(Function->Parameters.Num()==2);
    assert(Function->Parameters[0]->Type.BasicType->IsDelegate());
    assert(Function->Parameters[1]->Type.BasicType->IsDelegate());
    return Function;
}

std::shared_ptr<CFunctionDescription> CTypeDescription::GetDelegateRemoveFunction()
{
    auto Function=Functions[4];
    assert(Function && "Delegate must have a function");
    assert(Function->Parameters.Num()==2);
    assert(Function->Parameters[0]->Type.BasicType->IsDelegate());
    assert(Function->Parameters[1]->Type.BasicType->IsDelegate());
    return Function;    
}

std::shared_ptr<CFunctionDescription> CTypeDescription::GetDelegateClearFunction()
{
    auto Function=Functions[5];
    assert(Function && "Delegate must have a function");
    assert(Function->Parameters.Num()==0);
    return Function;    
}

// SDecoratedTypeDescription CTypeDescription::GetDelegateReturnType() const
// {
//     return GenericArguments.Arguments[0];
// }
